﻿// wordcount.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <fstream>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <list>
#include <array>
#include <algorithm>
#include <memory>
#include <cstdlib>
#include <clocale>
#include <cstring>
#include <thread>
#include <mutex>
#include <functional>
#include <cwchar>

#include <process.h>

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers

#include <Windows.h>

#define PRINT_FUNCTION_NAME do {\
  std::wstring wname;\
wprintf_s(L"函数名字：%ls\n", utf82wide(wname, __FUNCTION__).c_str());\
wname.clear();\
wprintf_s(L"函数名字：%ls\n", utf82wide(wname, __func__).c_str());\
wname.clear();\
wprintf_s(L"Decorated 修饰名字：%ls\n", utf82wide(wname, __FUNCDNAME__).c_str());\
wname.clear();\
wprintf_s(L"函数签名：%ls\n", utf82wide(wname, __FUNCSIG__).c_str());\
wname.clear();\
wprintf_s(L"编译日期和时间：%ls", utf82wide(wname, __DATE__).c_str());\
wname.clear();\
wprintf_s(L" %ls\n\n", utf82wide(wname, __TIME__).c_str());\
} while (0)

static std::wstring &utf82wide(std::wstring &wide_name, const char *utf8_name)
{
  int required_size{ 0 };
  size_t length{ strnlen_s(utf8_name, _MAX_PATH) };

  // length 返回的长度里没有空字符 '\0'，所以导致下面这行代码返回的结果里不含空字符
  required_size = MultiByteToWideChar(CP_UTF8, 0LU, utf8_name, static_cast<int>(length), nullptr, 0);

  if (required_size <= 0)
    return wide_name;

  wide_name.resize(static_cast<size_t>(required_size));

  MultiByteToWideChar(CP_UTF8, 0LU, utf8_name, static_cast<int>(length), wide_name.data(), static_cast<int>(wide_name.size()));
  return wide_name;
}

struct arg_set
{
  wchar_t *fname;
  int count;
};

struct arg_set *mailbox;

CRITICAL_SECTION lock;
CONDITION_VARIABLE flag;

int wmain(int argc, wchar_t *argv[], wchar_t *env[])
{
  _wsetlocale(LC_ALL, L"");
  std::ios_base::sync_with_stdio(true);

  if (argc != 3) {
    fwprintf_s(stderr, L"usage: %s file1 file2\n", argv[0]);
    exit(1);
  }

  struct arg_set args1, args2;
  uintptr_t t1{ 0ULL }, t2{ 0LLU };
  unsigned int t1id{ 0u }, t2id{ 0u };

  unsigned int __stdcall count_words(void *);
  int repots_in{ 0 }, total_words{ 0 };

  InitializeCriticalSection(&lock);
  InitializeConditionVariable(&flag);

  EnterCriticalSection(&lock);

  args1.fname = argv[1];
  args1.count = 0;
  t1 = _beginthreadex(nullptr, 0u, count_words, &args1, 0u, &t1id);

  args2.fname = argv[2];
  args2.count = 0;
  t2 = _beginthreadex(nullptr, 0u, count_words, &args2, 0u, &t2id);

  fputws(L"这里也可以不再调用 EnterCriticalSection(&lock) 了吗？\n", stdout);

  while (repots_in < 2) {
    _putws(L"MAIN: waiting for flag to go up");
    while (mailbox == nullptr) {
      SleepConditionVariableCS(&flag, &lock, INFINITE);
    }
    _putws(L"MAIN: Wow! flag was raised, I have the lock");
    wprintf_s(L"%7d: %ls\n", mailbox->count, mailbox->fname);
    total_words += mailbox->count;
    if (mailbox == &args1) {
      WaitForSingleObjectEx(reinterpret_cast<HANDLE>(t1), INFINITE, FALSE);
    }
    if (mailbox == &args2) {
      WaitForSingleObjectEx(reinterpret_cast<HANDLE>(t2), INFINITE, FALSE);
    }
    mailbox = nullptr;
    WakeConditionVariable(&flag);
    ++repots_in;
  }

  LeaveCriticalSection(&lock);
  DeleteCriticalSection(&lock);

  CloseHandle(reinterpret_cast<HANDLE>(t1));
  CloseHandle(reinterpret_cast<HANDLE>(t2));

  wprintf_s(L"%7d: total words\n", total_words);

  return EXIT_SUCCESS;
}

unsigned int __stdcall count_words(void *a)
{
  struct arg_set *args{ reinterpret_cast<struct arg_set *>(a) };

  FILE *fp{ nullptr };
  wint_t wc, prevwc = L'\0';

  if (_wfopen_s(&fp, args->fname, L"rt") != 0 || fp == nullptr) {
    _endthreadex(0U);
    return 0u;
  }

  while ((wc = fgetwc(fp)) != WEOF) {
    if (!iswalnum(wc) && iswalnum(prevwc))
      ++args->count;
    prevwc = wc;
  }
  fclose(fp);

  _putws(L"COUNT: waiting to get lock");
  EnterCriticalSection(&lock);
  _putws(L"COUNT: have lock, storing data");
  while (mailbox != nullptr) {
    _putws(L"COUNT: mailbox is not empty, waiting to free");
    SleepConditionVariableCS(&flag, &lock, INFINITE);
  }
  mailbox = args;
  _putws(L"COUNT: unlocking box");
  LeaveCriticalSection(&lock);
  _putws(L"COUNT: mailfox is free storing the mail and raising flag");
  WakeConditionVariable(&flag);
  _endthreadex(0u);
  return 0u;
}

// Run program: Ctrl + F5 or Debug > Start Without Debugging menu
// Debug program: F5 or Debug > Start Debugging menu

// Tips for Getting Started: 
//   1. Use the Solution Explorer window to add/manage files
//   2. Use the Team Explorer window to connect to source control
//   3. Use the Output window to see build output and other messages
//   4. Use the Error List window to view errors
//   5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project
//   6. In the future, to open this project again, go to File > Open > Project and select the .sln file
